﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using BrainTechLLC;
using System.Windows.Threading;
using System.Windows;
using BrainTechLLC.ThreadSafeObjects;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;

namespace BrainTechLLC.WPF
{
    [Serializable]
    [DataContract]
    public partial class BaseNotifyPropertyChangedUI : BaseNotifyPropertyChanged, INotifyPropertyChanged
    {
        /// <summary>
        /// Raise the property changed event on the UI thread
        /// </summary>
        /// <param name="propertyName"></param>
        public void RaisePropertyChangedUIThread(string propertyName)
        {
            OnPropertyChangedUIThread(propertyName);
        }

        /// <summary>
        /// Protected method used to raise property changed event on UI thread
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChangedUIThread(string propertyName)
        {
            if (!_gotDispatcher)
            {
                lock (_lock)
                {
                    if (!_gotDispatcher)
                    {
#if SILVERLIGHT
                        _dispatcher = Application.Current.RootVisual.Dispatcher;
#else
                        _dispatcher = Application.Current.Dispatcher;
#endif

                        _gotDispatcher = true;
                    }
                }
            }

            int suspend = GetSuspendPropertyChanged();

            if (suspend <= 0)
            {
#if SILVERLIGHT
#else
                if (_dispatcher == null || Thread.CurrentThread == _dispatcher.Thread)
                {
                    OnPropertyChanged(propertyName, false);
                }
                else
#endif
                {
                    _dispatcher.BeginInvoke(new OneParamDelegate((o) =>
                    {
                        try
                        {
                            string prop = o as string;
                            OnPropertyChanged(prop, false);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.ToString());
                        }
                    }),
#if SILVERLIGHT
#else
 _priority,
#endif
 propertyName);
                }
            }
            else
            {
                AddPropertyChanged(this, propertyName, true);
            }
        }

        /// <summary>
        /// Protected method used to raise multiple property changed events on the UI thread
        /// </summary>
        /// <param name="propertyNames"></param>
        protected void OnPropertyChangedUIThread(params string[] propertyNames)
        {
            if (!_gotDispatcher)
            {
                lock (_lock)
                {
                    if (!_gotDispatcher)
                    {
#if SILVERLIGHT
                        _dispatcher = Application.Current.RootVisual.Dispatcher;
#else
                        _dispatcher = Application.Current.Dispatcher;
#endif
                        _gotDispatcher = true;
                    }
                }
            }

            int suspend = GetSuspendPropertyChanged();

            if (suspend <= 0)
            {
                if (_dispatcher == null)
                {
                    foreach (var propertyName in propertyNames)
                    {
                        OnPropertyChanged(propertyName, false);
                    }
                }
                else
                {
#if SILVERLIGHT
                    _dispatcher.BeginInvoke(new OneParamDelegate((o) =>
                    {
                        try
                        {
                            List<string> propNames = o as List<string>;
                            foreach (var propertyName in propNames)
                            {
                                OnPropertyChanged(false, propertyName);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.ToString());
                        }
                    }), propertyNames.ToList());
#else
                    _dispatcher.BeginInvoke(new OneParamDelegate((o) =>
                    {
                        try
                        {
                            List<string> propNames = o as List<string>;
                            foreach (var propertyName in propNames)
                            {
                                OnPropertyChanged(false, propertyName);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.ToString());
                        }
                    }), _priority, propertyNames.ToList());
#endif
                }
            }
            else
            {
                var collection = GetPropertyChanges();
                foreach (var propertyName in propertyNames)
                {
                    collection.AddPropertyChange(new ClassAndPropertyChangeUI(this, propertyName, true));
                }
            }
        }

        protected override void ReadPropertyChanges(string[] propertyNames)
        {
            var collection = GetPropertyChanges();
            foreach (var propertyName in propertyNames)
            {
                collection.AddPropertyChange(new ClassAndPropertyChangeUI(this, propertyName, false));
            }
        }
    }

    [Serializable]
    [DataContract]
    public class ClassAndPropertyChangeUI : ClassAndPropertyChange
    {
        [DataMember]
        public bool UIThread;

        public override bool EqualsOther(ClassAndPropertyChange o)
        {
            ClassAndPropertyChangeUI other = o as ClassAndPropertyChangeUI;

            if (other == null)
                return base.EqualsOther(o);

            return (PropertyName == other.PropertyName) && (UIThread = other.UIThread) && (ClassObject == other.ClassObject);
        }

        public ClassAndPropertyChangeUI(BaseNotifyPropertyChanged classObject, string propertyName, bool uiThread)
            : base(classObject, propertyName)
        {
            UIThread = uiThread;
        }

        public override void RaisePropertyChanged()
        {
#if SILVERLIGHT
            if (!UIThread)
            {
                ClassObject.RaisePropertyChanged(PropertyName);
            }
            // TODO: 
            //else
            //{
            //    ClassObject.RaisePropertyChangedUIThread(PropertyName);
            //}
#else
            if (ClassObject._propertyChanged != null && ClassObject._propertyChanged.eventEntries.Count > 0)
            {
                if (!UIThread)
                {
                    ClassObject.RaisePropertyChanged(PropertyName);
                }
                else
                {
                    BaseNotifyPropertyChangedUI notifyUI = ClassObject as BaseNotifyPropertyChangedUI;
                    if (notifyUI != null)
                    {
                        notifyUI.RaisePropertyChangedUIThread(PropertyName);
                    }
                    else
                    {
                        ClassObject.RaisePropertyChanged(PropertyName);
                    }
                }
            }
#endif
        }
    }

    public partial class BaseNotifyPropertyChangedUI : INotifyPropertyChanged
    {
        public override void AddPropertyChanged(BaseNotifyPropertyChanged classObject, string propertyName)
        {
            AddPropertyChanged(classObject, propertyName, false);
        }

        public void AddPropertyChanged(BaseNotifyPropertyChanged classObject, string propertyName, bool uiThread)
        {
            var collection = GetPropertyChanges();
            ClassAndPropertyChangeUI p = new ClassAndPropertyChangeUI(classObject, propertyName, uiThread);
            collection.AddPropertyChange(p);
        }
    }

}
